<!doctype html>
<title>MediaStreamTrack and InputDeviceInfo GetCapabilities</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=/resources/testdriver.js></script>
<script src=/resources/testdriver-vendor.js></script>
<script src=permission-helper.js></script>
<script>

const audioProperties = [
  {name: "sampleRate", type: "number"},
  {name: "sampleSize", type: "number"},
  {name: "echoCancellation", type: "boolean"},
  {name: "autoGainControl", type: "boolean"},
  {name: "noiseSuppression", type: "boolean"},
  {name: "voiceIsolation", type: "boolean"},
  {name: "latency", type: "number"},
  {name: "channelCount", type: "number"},
  {name: "deviceId", type: "string"},
  {name: "groupId", type: "string"}
];

const videoProperties = [
  {name: "width", type: "number"},
  {name: "height", type: "number"},
  {name: "aspectRatio", type: "number"},
  {name: "frameRate", type: "number"},
  {name: "facingMode", type: "enum-any", validValues: ["user", "environment", "left", "right"]},
  {name: "resizeMode", type: "enum-all", validValues: ["none", "crop-and-scale"]},
  {name: "deviceId", type: "string"},
  {name: "groupId", type: "string"},
];

function verifyBooleanCapability(capability) {
  assert_less_than_equal(capability.length, 2);
  capability.forEach(c => assert_equals(typeof c, "boolean"));
}

function verifyNumberCapability(capability) {
    assert_equals(typeof capability, "object");
    assert_equals(Object.keys(capability).length, 2);
    assert_true(capability.hasOwnProperty('min'));
    assert_true(capability.hasOwnProperty('max'));
    assert_less_than_equal(capability.min, capability.max);
}

// Verify that any value provided by an enum capability is in the set of valid
// values.
function verifyEnumAnyCapability(capability, enumMembers) {
  capability.forEach(c => {
    assert_equals(typeof c, "string");
    assert_in_array(c, enumMembers);
  });
}

// Verify that all required values are supported by a capability.
function verifyEnumAllCapability(capability, enumMembers, testNamePrefix) {
  enumMembers.forEach(member => {
    test(() => {
      assert_in_array(member, capability);
    }, testNamePrefix + " Value: " + member);
  });
}

function testCapabilities(capabilities, property, testNamePrefix) {
  let testName = testNamePrefix + " " + property.name;
  test(() => {
    assert_true(capabilities.hasOwnProperty(property.name));
  }, testName + " property present.");

  const capability = capabilities[property.name];
  testName += " properly supported.";
  if (property.type == "string") {
    test(() => {
      assert_equals(typeof capability, "string");
    }, testName);
  }

  if (property.type == "boolean") {
    test(() => {
      verifyBooleanCapability(capability);
    }, testName);
  }

  if (property.type == "number") {
    test(() => {
      verifyNumberCapability(capability);
    }, testName);
  }

  if (property.type.startsWith("enum")) {
    test(() => {
      verifyEnumAnyCapability(capability, property.validValues);
    }, testName);

    if (property.type == "enum-all") {
      verifyEnumAllCapability(capability, property.validValues, testName);
    }
  }
}

{
  audioProperties.forEach((property, i) => {
    promise_test(async t => {
      if (i === 0) await setMediaPermission("granted", ["microphone"]);
      const stream = await navigator.mediaDevices.getUserMedia({audio: true});
      t.add_cleanup(() => stream.getAudioTracks()[0].stop());
      const audioCapabilities = stream.getAudioTracks()[0].getCapabilities();
      testCapabilities(audioCapabilities, property, "Audio track getCapabilities()");
    }, "Setup audio MediaStreamTrack getCapabilities() test for " + property.name);
  });

  videoProperties.forEach(property => {
    promise_test(async t => {
      const stream = await navigator.mediaDevices.getUserMedia({video: true});
      t.add_cleanup(() => stream.getVideoTracks()[0].stop());
      const audioCapabilities = stream.getVideoTracks()[0].getCapabilities();
      testCapabilities(audioCapabilities, property, "Video track getCapabilities()");
    }, "Setup video MediaStreamTrack getCapabilities() test for " + property.name);
  });
}

{
  audioProperties.forEach(property => {
    promise_test(async t => {
      const devices = await navigator.mediaDevices.enumerateDevices();
      for (const device of devices) {
        // Test only one device.
        if (device.kind == "audioinput") {
          assert_inherits(device, "getCapabilities");
          const capabilities = device.getCapabilities();
          testCapabilities(capabilities, property, "Audio device getCapabilities()");
          break;
        }
      }
    }, "Setup audio InputDeviceInfo getCapabilities() test for " + property.name);
  });

  videoProperties.forEach(property => {
    promise_test(async t => {
      const devices = await navigator.mediaDevices.enumerateDevices();
      for (const device of devices) {
        // Test only one device.
        if (device.kind == "videoinput") {
          assert_inherits(device, "getCapabilities");
          const capabilities = device.getCapabilities();
          testCapabilities(capabilities, property, "Video device getCapabilities()");
          break;
        }
      }
    }, "Setup video InputDeviceInfo getCapabilities() test for " + property.name);
  });
}
</script>
